package emulator.hardware.custom;

import emulator.hardware.HwWord;
import emulator.hardware.bus.Bus;
import emulator.hardware.debug.BusWatchException;
import emulator.hardware.memory.DebugMemoryFlags;
import emulator.hardware.memory.DebugMemoryInterface;
import emulator.hardware.memory.MemoryBlock;
import emulator.hardware.memory.UnmappedMemoryException;

public class Fe3Memory extends MemoryBlock
{
//	private static final int START_MODE = 0x00;
//	private static final int FLASH_MODE = 0x20;
//	private static final int SUPER_ROM_MODE = 0x40;
//	private static final int RAM_ROM_MODE = 0x60;
//	private static final int RAM1_MODE = 0x80;
	private static final int SUPER_RAM_MODE = 0xA0;
//	private static final int RAM2_MODE = 0xC0;
	
	private static final int NUM_1K_BLOCKS = 64;
	
	int block1k_offsets_r[] = new int[NUM_1K_BLOCKS];
	int block1k_offsets_w[] = new int[NUM_1K_BLOCKS];
	boolean block1k_readonly[] = new boolean[NUM_1K_BLOCKS];
	
	int reg1 = 0;
	int reg2 = 0;
	
	public Fe3Memory()
	{
		super((512+64)*1024);
		initMapping();
	}

	private void initMapping() {
		for (int i = 0; i < NUM_1K_BLOCKS; i++)
		{
			setBlockRW(i, i*1024);
			block1k_readonly[i] = false;
		}
	}

	private void set3kToBank0() {
		for (int i = 1; i < 4; i++)
			setBlockRW(i, (64+i)*1024);
	}

	private void selectBank(int bank)
	{
		for (int i = 0; i < 24; i++)
			setBlockRW(8+i, (64+i+bank*32)*1024);
		for (int i = 0; i < 8; i++)
			setBlockRW(40+i, (88+i+bank*32)*1024);
	}
	
	private void setBlockRW(int i, int offset)
	{
		block1k_offsets_r[i] = offset;
		block1k_offsets_w[i] = offset;
	}

	private void onReg1() {
		switch(reg1 & 0xE0)
		{
		case SUPER_RAM_MODE:
//			set3kToBank0();
			selectBank(reg1&0x1F);
			break;
		}
		
	}
	
	private int readRAM(int bus_address) throws UnmappedMemoryException, BusWatchException
	{
		int address = block1k_offsets_r[bus_address/1024] + (bus_address & 0x3ff);
		return read(address);
	}

	private void writeRAM(int bus_address, int data) throws UnmappedMemoryException, BusWatchException
	{
		int address = block1k_offsets_w[bus_address/1024] + (bus_address & 0x3ff);
		write(address, data);
	}
	
	private void onReg2() {
		// TODO Auto-generated method stub
		
	}
	
	public Bus getMemoryBus()
	{
		return new MemoryBus();
	}
	
	public Bus getIoBus()
	{
		return new IoBus();
	}

	class MemoryBus implements Bus, DebugMemoryInterface
	{
		DebugMemoryFlags [] flags;

		MemoryBus() {
			flags = new DebugMemoryFlags [NUM_1K_BLOCKS*1024];
			for (int i = 0; i < flags.length; i++)
				flags[i] = new DebugMemoryFlags();
		}
		
		@Override
		public void write(int address, int data) 
			throws BusWatchException, UnmappedMemoryException {
			if (address >= flags.length)
				throw new UnmappedMemoryException(new HwWord(address));
			if (flags[address].isWriteWatch())
				throw new BusWatchException("write", new HwWord(address));
			if (!flags[address].isReadOnly())
				writeRAM(address, data);
		}

		@Override
		public int read(int address)
			throws BusWatchException, UnmappedMemoryException {
			if (address >= flags.length)
				throw new UnmappedMemoryException(new HwWord(address));
			if (flags[address].isReadWatch())
				throw new BusWatchException("read", new HwWord(address));
			return readRAM(address);
		}

		@Override
		synchronized public void enableReadWatch(int first, int last, boolean enable) {
			for (int i = first; i <= last; i++)
				flags[i].setReadWatch(enable);
		}

		@Override
		synchronized public void enableWriteWatch(int first, int last, boolean enable) {
			for (int i = first; i <= last; i++)
				flags[i].setWriteWatch(enable);
		}

		@Override
		public void enableReadonly(int first, int last, boolean enable) {
			for (int i = first; i <= last; i++)
				flags[i].setReadOnly(enable);
		}
	}
	
	class IoBus implements Bus
	{
		private static final int REG1_ADDRESS = 0x9C02;
		private static final int REG2_ADDRESS = 0x9C03;

		@Override
		public void write(int address, int data) throws BusWatchException,
				UnmappedMemoryException {
			if (address == REG1_ADDRESS)
				setReg1(data);
			else if (address == REG2_ADDRESS)
				setReg2(data);
		}

		@Override
		public int read(int address) throws BusWatchException,
				UnmappedMemoryException {
			if (address == REG1_ADDRESS)
				return getReg1();
			else if (address == REG2_ADDRESS)
				return getReg2();
			return (address >> 8) & 0xFF;
		}
	}

	public int getReg1() {
		return reg1;
	}

	public void setReg1(int reg1) {
		this.reg1 = reg1;
		onReg1();
	}

	public int getReg2() {
		return reg2;
	}

	public void setReg2(int reg2) {
		this.reg2 = reg2;
		onReg2();
	}

}
